package management;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.rmi.NoSuchObjectException;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.text.DecimalFormat;
import java.util.Iterator;

import analytics.AnalyticsServer;
import billing.Bill;
import billing.BillingServer;
import billing.BillingServerSecure;
import billing.PriceStep;
import billing.PriceSteps;

/**
 * Management client.
 * 
 * @author Lukas
 *
 */
public class Client {
	
	private static String registryHost;
	private static int registryPort;
	
	private static AnalyticsServer analyticsServer;
	private static BillingServer billingServer;
	
	private static ClientEventReceiver receiver;
	private static ClientEventReceiverImpl receiverImpl;
	
	private static boolean running;

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		if (args.length != 2 && args.length != 3) {
			System.out.println("usage: java management.client <RMI analyticsName> <RMI billingName>");
			return;
		}
		if (!loadProperties()) {
			return;
		}
		
		receiverImpl = new ClientEventReceiverImpl();
		try {
			receiver = (ClientEventReceiver) UnicastRemoteObject.exportObject(receiverImpl, 0);
		} catch (RemoteException e) {
			System.err.println("ERROR: Could not export ClientEventReceiver");
			return;
		}
		
        analyticsServer = null;
        billingServer = null;
		try {
            Registry registry = LocateRegistry.getRegistry(registryHost, registryPort);

			analyticsServer = (AnalyticsServer) registry.lookup(args[0]);
			billingServer = (BillingServer) registry.lookup(args[1]);

			//for load test////////////////////////////////////////////////////////////
			if (args.length == 3) {
				receiverImpl.setHide(false);
				analyticsServer.subscribe(".*", receiver);
				try {
					Thread.sleep(Long.parseLong(args[2]));
				} catch (NumberFormatException e) {
					e.printStackTrace();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			} else
			///////////////////////////////////////////////////////////////////////////
			
			processUserInput();
		} catch (RemoteException e) {
			
		} catch (NotBoundException e) {
			if (analyticsServer == null) {
	            System.err.println("ERROR: Could not find RMI-object AnalyticsServer");
			} else {
				System.err.println("ERROR: Could not find RMI-object BillingServer");
			}
		} catch (IOException e) {
			System.err.println("IO-Exception: " + e.getMessage());
		} finally {
			try {
				UnicastRemoteObject.unexportObject(receiverImpl, true);
			} catch (NoSuchObjectException e) {
				System.err.println("ERROR: Could not unexport receiver");
			}
		}
	}
	
	private static void processUserInput() throws IOException {
		BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
		running = true;
		String username = "";
		BillingServerSecure billingServerSecure = null;
		do {
			System.out.print(username + "> ");
			String command = input.readLine();
			String[] commandPart = command.split("\\s+");
//!login
			if (commandPart[0].compareToIgnoreCase("!login") == 0) {
				if (commandPart.length != 3) {
					System.out.println("usage: !login <username> <password>");
					continue;
				}
				if (username != "") {
					System.out.println("You have to logout first");
					continue;
				}
				billingServerSecure = billingServer.login(commandPart[1], commandPart[2]);
				if (billingServerSecure != null) {
					username = commandPart[1];
					System.out.println("Successfully logged in as " + username);
				} else {
					System.out.println("Could not login as " + username);
				}
//!steps
			} else if (commandPart[0].compareToIgnoreCase("!steps") == 0) {
				if (commandPart.length != 1) {
					System.out.println("usage: !steps");
					continue;
				}
				if (billingServerSecure == null) {
					System.out.println("ERROR: You are currently not logged in");
					continue;
				}
				PriceSteps priceSteps = billingServerSecure.getPriceSteps();
				Iterator<PriceStep> iter = priceSteps.getSteps().iterator();
				System.out.println("Min_Price|Max_Price|Fee_Fixed|Fee_Variable");
				if (!iter.hasNext()) {
					System.out.println("no entries found");
				}
				while (iter.hasNext()) {
					PriceStep ps = iter.next();
					System.out.print(stringFixLength(doubleToStrF(ps.getStartPrice(), 3), 10));
					if (ps.getEndPrice() == Double.POSITIVE_INFINITY) {
						System.out.print("INFINITY  ");
					} else {
						System.out.print(stringFixLength(doubleToStrF(ps.getEndPrice(), 3), 10));
					}
					System.out.print(stringFixLength(doubleToStrF(ps.getFixedPrice(), 1), 10));
					System.out.println(doubleToStrF(ps.getVariablePricePercent(), 1) + '%');
				}
//!addStep
			} else if (commandPart[0].compareToIgnoreCase("!addStep") == 0) {
				if (commandPart.length != 5) {
					System.out.println("usage: !addStep <startPrice> <endPrice> <fixedPrice> <variablePricePercent>");
					continue;
				}
				if (billingServerSecure == null) {
					System.out.println("ERROR: You are currently not logged in");
					continue;
				}
				String step = "";
				try {
					double startPrice = Double.parseDouble(commandPart[1]);
					double endPrice = Double.parseDouble(commandPart[2]);
					double fixedPrice = Double.parseDouble(commandPart[3]);
					double variablePrice = Double.parseDouble(commandPart[4]);
					if (endPrice == 0d) {
						step = "[" + startPrice + " INFINITY]";
					} else {
						step = "[" + startPrice + " " + endPrice + "]";
					}
					billingServerSecure.createPriceStep(startPrice, endPrice, fixedPrice, variablePrice);
					System.out.println("Step " + step + " successfully added");
				} catch (RemoteException e) {
					System.out.println("Could not create step " + step);
				}
//!removeStep
			} else if (commandPart[0].compareToIgnoreCase("!removeStep") == 0) {
				if (commandPart.length != 3) {
					System.out.println("usage: !removeStep <startPrice> <endPrice>");
					continue;
				}
				if (billingServerSecure == null) {
					System.out.println("ERROR: You are currently not logged in");
					continue;
				}
				double startPrice = 0d;
				double endPrice = 0d;
				String step = "";
				try {
					startPrice = Double.parseDouble(commandPart[1]);
					endPrice = Double.parseDouble(commandPart[2]);
					if (endPrice == 0d) {
						step = "[" + startPrice + " INFINITY]";
					} else {
						step = "[" + startPrice + " " + endPrice + "]";
					}
					billingServerSecure.deletePriceStep(startPrice, endPrice);
					System.out.println("Price step " + step + " successfully removed");
				} catch (RemoteException e) {
					System.out.println("ERROR: Price step " + step + " does not exist");
				}
//!bill
			} else if (commandPart[0].compareToIgnoreCase("!bill") == 0) {
				if (commandPart.length != 2) {
					System.out.println("usage: !bill <username>");
					continue;
				}
				if (billingServerSecure == null) {
					System.out.println("ERROR: You are currently not logged in");
					continue;
				}
				Bill bill = billingServerSecure.getBill(commandPart[1]);
				if (bill == null) {
					System.out.println("No bill found");
				} else {
					System.out.println(bill);
				}
//!logout
			} else if (commandPart[0].compareToIgnoreCase("!logout") == 0) {
				if (commandPart.length != 1) {
					System.out.println("usage: !logout");
					continue;
				}
				if (billingServerSecure == null) {
					System.out.println("You are already logged out");
					continue;
				}
				username = "";
				billingServerSecure = null;
				System.out.println("successfully logged out");
//!subscribe
			} else if (commandPart[0].compareToIgnoreCase("!subscribe") == 0) {
				commandPart = command.split("'");
				if (commandPart.length != 2) {
					System.out.println("usage: !subscribe '<regular expression>'");
					continue;
				}
				String id = analyticsServer.subscribe(commandPart[1], receiver);
				System.out.println("Created subscription with ID " + id + " for events using filter '" + commandPart[1] + "'");
//!unsubscribe
			} else if (commandPart[0].compareToIgnoreCase("!unsubscribe") == 0) {
				if (commandPart.length != 2) {
					System.out.println("usage: !unsubscribe <id>");
					continue;
				}
				if (analyticsServer.unsubscribe(commandPart[1])) {
					System.out.println("subscription " + commandPart[1] + " terminated");
				}
//!auto
			} else if (commandPart[0].compareToIgnoreCase("!auto") == 0) {
				receiverImpl.print();
				receiverImpl.setHide(false);
				System.out.println("set event notification mode to auto");
//!hide
			} else if (commandPart[0].compareToIgnoreCase("!hide") == 0) {
				receiverImpl.setHide(true);
				System.out.println("set event notification mode to hide");
//!print
			} else if (commandPart[0].compareToIgnoreCase("!print") == 0) {
				receiverImpl.print();
//!exit
			} else if (commandPart[0].compareToIgnoreCase("!exit") == 0) {
				running = false;
			} else {
				System.out.println("unknown command");
			}
		} while (running);
	}
	
	private static String doubleToStrF(double d, int decimalPlaces) {
		StringBuilder pattern = new StringBuilder("0.");
		for (int i = 0; i < decimalPlaces; i++) {
			pattern.append('#');
		}
		DecimalFormat dec = new DecimalFormat(pattern.toString());
		return dec.format(d);
	}
	
	private static String stringFixLength(String s, int length) {
		StringBuilder sb = new StringBuilder(s);
		for (int i = s.length(); i < length; i++) {
			sb.append(' ');
		}
		return sb.toString();
	}

	
	
	
	private static boolean loadProperties() {
		boolean ok = false;
		java.io.InputStream is = ClassLoader.getSystemResourceAsStream("registry.properties");
		if (is != null) {
			java.util.Properties props = new java.util.Properties();
			try {
				props.load(is);
				registryHost = props.getProperty("registry.host");
				registryPort = Integer.parseInt(props.getProperty("registry.port"));
				ok = true;
			} catch (IOException e) {
				System.err.println("Could not load registry.properties");
			} catch (NumberFormatException e) {
				System.err.println("registry.port in registry.properties must be integer");
			}
			finally {
				try {
					is.close();
				} catch (IOException e) {
					System.err.println("could not close registry.properties");
				}
			}
		} else {
			System.err.println("Properties file not found!");
		}
		return ok;
	}
}
